﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.Windows.Media.Imaging;

using _3DTools;

using SBPweb.MediaCenter.Tools.WPF.Extenders;
using SBPweb.MediaCenter.Tools.WPF.Helpers;

namespace SBPweb.MediaCenter.Tools.WPF.Controls
{

    public class CarouselListViewer<TList> where TList : IList, ICollection
    {

        #region Private fields

        private TList list;
        private ModelVisual3D visual;
        private Camera camera;

        private Vector3D axis = new Vector3D(0, 1, 0);
        private double radius = 1.0f;
        private TimeSpan animationSpeed = TimeSpan.FromSeconds(1);

        private Dictionary<UserControl, Visual3D> controlDictionary = new Dictionary<UserControl, Visual3D>();
        private Dictionary<Visual3D, UserControl> visualDictionary = new Dictionary<Visual3D, UserControl>();

        private int selectedIndex;
        private bool keepNormals = true;
        private bool isInteractive = true;

        #endregion

        #region Constructor logic

        public CarouselListViewer(ModelVisual3D visual, Camera camera)
            : this(visual, camera, Activator.CreateInstance<TList>())
        {
        }

        public CarouselListViewer(ModelVisual3D visual, Camera camera, TList list)
        {
            this.visual = visual;
            this.camera = camera;
            this.list = list;
        }

        #endregion

        #region Property accessors

        public TList List
        {
            get
            {
                return list;
            }
        }

        public ModelVisual3D Visual
        {
            get
            {
                return visual;
            }
            set
            {
                visual = value;
            }
        }

        public double Radius
        {
            get
            {
                return radius;
            }
            set
            {
                radius = value;
            }
        }

        public Vector3D Axis
        {
            get
            {
                return axis;
            }
            set
            {
                axis = value;
            }
        }

        public TimeSpan AnimationSpeed
        {
            get
            {
                return animationSpeed;
            }
            set
            {
                animationSpeed = value;
            }
        }

        public int SelectedIndex
        {
            get
            {
                return selectedIndex;
            }
            set
            {
                int newIndex = value;
                if (newIndex >= visual.Children.Count)
                {
                    newIndex = 0;
                }
                if (newIndex < 0)
                {
                    newIndex = visual.Children.Count - 1;
                }
                selectedIndex = newIndex;
                MaintainPositions();
                if (SelectedIndexChanged != null)
                {
                    SelectedIndexChanged(this, EventArgs.Empty);
                }
            }
        }

        public bool KeepNormals
        {
            get
            {
                return keepNormals;
            }
            set
            {
                keepNormals = value;
            }
        }

        public bool IsInteractive
        {
            get
            {
                return isInteractive;
            }
            set
            {
                isInteractive = value;
            }
        }

        #endregion

        #region Events / delegates

        public event MeshGeometryPopulationHandler MeshPopulating;

        public delegate void MeshGeometryPopulationHandler(object sender, out Geometry3D geometry);

        public event EventHandler SelectedIndexChanged;

        #endregion

        #region Methods

        protected void Add(UserControl control)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Add(control); });
            }
            else
            {
                Visual3D v3d = GetVisual3DForControl(control);
                visual.Children.Add(v3d);
                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        protected void Add(Visual3D visual3d)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Add(visual3d); });
            }
            else
            {
                visual.Children.Add(visual3d);
                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        protected void Remove(UserControl control)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Remove(control); });
            }
            else
            {
                Visual3D v3d = GetVisual3DForControl(control);
                visual.Children.Remove(v3d);
                lock (controlDictionary)
                {
                    controlDictionary.Remove(control);
                    visualDictionary.Remove(v3d);
                }
                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        protected void Remove(Visual3D visual3d)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Remove(visual3d); });
            }
            else
            {
                visual.Children.Remove(visual3d);

                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        #region Private helper functions

        private Visual3D GetVisual3DForControl(UserControl control)
        {
            lock (controlDictionary)
            {
                if (controlDictionary.ContainsKey(control))
                {
                    return controlDictionary[control];
                }
                else
                {
                    Visual3D ret = GenerateVisual3DForControl(control);
                    controlDictionary.Add(control, ret);
                    visualDictionary.Add(ret, control);
                    return ret;
                }
            }
        }

        private Visual3D GenerateVisual3DForControl(UserControl control)
        {
            Geometry3D geom = null;
            if (MeshPopulating != null)
            {
                MeshPopulating(this, out geom);
            }
            if (isInteractive)
            {
                InteractiveVisual3D iv3d = new InteractiveVisual3D();
                iv3d.Visual = control;
                iv3d.Geometry = geom;
                iv3d.IsBackVisible = !KeepNormals;
                return iv3d;
            }
            else
            {
                Visual vis = control;
                Brush brush = null;

                TagDictionary td = TagDictionary.GetTag(control);
                if (!td.ContainsKey("AsImageSource"))
                {
                    #region THIS IS DAMN SLOW
                    //brush = new VisualBrush(control);
                    #endregion

                    #region Possible better, but not working
                    RenderTargetBitmap rtb = new RenderTargetBitmap((int)control.Width, (int)control.Height, 96d, 96d, PixelFormats.Pbgra32);
                    Canvas c = new Canvas();
                    (Application.Current.MainWindow.Content as Panel).Children.Add(c);
                    //c.Children.Add(control);
                    System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle();
                    rect.Width = 100;
                    rect.Height = 100;
                    rect.RenderTransform = null;
                    rect.ClipToBounds = false;
                    rect.Fill = new SolidColorBrush(Colors.Red);
                    c.Children.Add(rect);
                    c.HorizontalAlignment = HorizontalAlignment.Left;
                    c.VerticalAlignment = VerticalAlignment.Top;
                    c.Margin = new Thickness(0.0f);
                    c.ClipToBounds = true;
                    vis = c;
                    rtb.Render(vis);
                    brush = new ImageBrush(rtb);
                    #endregion
                }
                else
                {
                    brush = new ImageBrush(td.Get<string, BitmapSource>("AsImageSource"));
                }

                Material material = new DiffuseMaterial(brush);
                ModelVisual3D mv3d = new ModelVisual3D();
                mv3d.Content = new GeometryModel3D(geom, material);
                return mv3d;
            }
        }

        private void MaintainPositions()
        {
            lock (controlDictionary)
            {
                int count = visual.Children.Count;
                int i = 0;
                double angle = 0.0f;
                if (count != 0)
                {
                    angle = 360.0f / count;
                }
                foreach (Visual3D v3d in visual.Children)//(KeyValuePair<UserControl, Visual3D> kvp in controlDictionary)
                {
                    Transform3DGroup tranGroup = v3d.Transform as Transform3DGroup;
                    TranslateTransform3D transTrans;
                    RotateTransform3D transRot;
                    ScaleTransform3D transScale;
                    if (tranGroup == null)
                    {
                        v3d.Transform = tranGroup = new Transform3DGroup();
                        tranGroup.Children.Add(transTrans = new TranslateTransform3D());
                        tranGroup.Children.Add(transRot = new RotateTransform3D());
                        tranGroup.Children.Add(transScale = new ScaleTransform3D());
                    }
                    else
                    {
                        transTrans = tranGroup.Children[0] as TranslateTransform3D;
                        transRot = tranGroup.Children[1] as RotateTransform3D;
                        transScale = tranGroup.Children[2] as ScaleTransform3D;
                    }

                    double scXNew = 1.0f;
                    double scYNew = 1.0f;
                    double scZNew = 1.0f;
                    double offX3dNew = 0.0f;
                    double offY3dNew = 0.0f;
                    double offZ3dNew = 0.0f;

                    Duration duration = new Duration(animationSpeed);

                    TagDictionary tag;
                    lock (controlDictionary)
                    {
                        if (controlDictionary.ContainsValue(v3d))
                        {
                            UserControl ctrl = visualDictionary[v3d];
                            tag = TagDictionary.GetTag(ctrl);
                        }
                        else
                        {
                            tag = TagDictionary.GetTag(v3d);
                        }
                    }

                    if (tag != null)
                    {
                        Vector3D scale = tag.Get<string, Vector3D>("Scale", new Vector3D(1.0f, 1.0f, 1.0f));
                        Vector3D offset = tag.Get<string, Vector3D>("Offset");
                        Rotation3D rotation = tag.Get<string, Rotation3D>("Rotation");
                        scXNew = scale.X;
                        scYNew = scale.Y;
                        scZNew = scale.Z;
                        offX3dNew = offset.X * 1.0 / scXNew;
                        offY3dNew = offset.Y * 1.0 / scYNew;
                        if (rotation != null)
                        {
                            Rotation3DAnimation rotAnim = new Rotation3DAnimation(rotation, duration);
                            transRot.BeginAnimation(RotateTransform3D.RotationProperty, rotAnim);
                        }
                    }

                    if (i == selectedIndex)
                    {
                        scXNew *= 2.5f;
                        scYNew *= 2.5f;
                    }

                    if (KeepNormals)
                    {
                        Point p = Math2DHelper.GetPointOfCircle(new Point(axis.X, axis.Z), radius * 1.0 / scZNew, angle * (i - selectedIndex));
                        offX3dNew = p.X;
                        offY3dNew = 0.0;
                        offZ3dNew = p.Y;
                    }
                    else
                    {
                        Rotation3D rot3dNew = new AxisAngleRotation3D(axis, angle * (i - selectedIndex));
                        Rotation3DAnimation rotAnim = new Rotation3DAnimation(rot3dNew, duration);
                        transRot.BeginAnimation(RotateTransform3D.RotationProperty, rotAnim);
                        offZ3dNew = radius * 1.0 / scZNew;
                    }

                    DoubleAnimation transXAnim = new DoubleAnimation(offX3dNew, duration);
                    DoubleAnimation transYAnim = new DoubleAnimation(offY3dNew, duration);
                    DoubleAnimation transZAnim = new DoubleAnimation(offZ3dNew, duration);
                    transTrans.BeginAnimation(TranslateTransform3D.OffsetXProperty, transXAnim);
                    transTrans.BeginAnimation(TranslateTransform3D.OffsetYProperty, transYAnim);
                    transTrans.BeginAnimation(TranslateTransform3D.OffsetZProperty, transZAnim);

                    DoubleAnimation scaleX = new DoubleAnimation(scXNew, duration);
                    DoubleAnimation scaleY = new DoubleAnimation(scYNew, duration);
                    DoubleAnimation scaleZ = new DoubleAnimation(scZNew, duration);

                    transScale.BeginAnimation(ScaleTransform3D.ScaleXProperty, scaleX);
                    transScale.BeginAnimation(ScaleTransform3D.ScaleYProperty, scaleY);
                    transScale.BeginAnimation(ScaleTransform3D.ScaleZProperty, scaleZ);

                    i++;
                }
            }
        }

        #endregion

        #endregion

    }
    /*
    public class CarouselListViewer<TList> where TList : IList, ICollection
    {

        #region Private fields

        private TList list;
        private ModelVisual3D visual;
        private Camera camera;

        private Vector3D axis = new Vector3D(0, 1, 0);
        private double radius = 1.0f;
        private TimeSpan animationSpeed = TimeSpan.FromSeconds(1);

        private Dictionary<UserControl, Visual3D> controlDictionary = new Dictionary<UserControl, Visual3D>();
        private Dictionary<Visual3D, UserControl> visualDictionary = new Dictionary<Visual3D, UserControl>();

        private int selectedIndex;
        private bool keepNormals = true;
        private bool isInteractive = true;

        #endregion

        #region Constructor logic

        public CarouselListViewer(ModelVisual3D visual, Camera camera)
            : this(visual, camera, Activator.CreateInstance<TList>())
        {
        }

        public CarouselListViewer(ModelVisual3D visual, Camera camera, TList list)
        {
            this.visual = visual;
            this.camera = camera;
            this.list = list;
        }

        #endregion

        #region Property accessors

        public TList List
        {
            get
            {
                return list;
            }
        }

        public ModelVisual3D Visual
        {
            get
            {
                return visual;
            }
            set
            {
                visual = value;
            }
        }

        public double Radius
        {
            get
            {
                return radius;
            }
            set
            {
                radius = value;
            }
        }

        public Vector3D Axis
        {
            get
            {
                return axis;
            }
            set
            {
                axis = value;
            }
        }

        public TimeSpan AnimationSpeed
        {
            get
            {
                return animationSpeed;
            }
            set
            {
                animationSpeed = value;
            }
        }

        public int SelectedIndex
        {
            get
            {
                return selectedIndex;
            }
            set
            {
                int newIndex = value;
                if (newIndex >= visual.Children.Count)
                {
                    newIndex = 0;
                }
                if (newIndex < 0)
                {
                    newIndex = visual.Children.Count - 1;
                }
                selectedIndex = newIndex;
                MaintainPositions();
                if(SelectedIndexChanged != null)
                {
                    SelectedIndexChanged(this, EventArgs.Empty);
                }
            }
        }

        public bool KeepNormals
        {
            get
            {
                return keepNormals;
            }
            set
            {
                keepNormals = value;
            }
        }

        public bool IsInteractive
        {
            get
            {
                return isInteractive;
            }
            set
            {
                isInteractive = value;
            }
        }

        #endregion

        #region Events / delegates

        public event MeshGeometryPopulationHandler MeshPopulating;

        public delegate void MeshGeometryPopulationHandler(object sender, out Geometry3D geometry);

        public event EventHandler SelectedIndexChanged;

        #endregion

        #region Methods

        protected void Add(UserControl control)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Add(control); });
            }
            else
            {
                Visual3D v3d = GetVisual3DForControl(control);
                visual.Children.Add(v3d);
                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        protected void Add(Visual3D visual3d)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Add(visual3d); });
            }
            else
            {
                visual.Children.Add(visual3d);
                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        protected void Remove(UserControl control)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Remove(control); });
            }
            else
            {
                Visual3D v3d = GetVisual3DForControl(control);
                visual.Children.Remove(v3d);
                lock (controlDictionary)
                {
                    controlDictionary.Remove(control);
                    visualDictionary.Remove(v3d);
                }
                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        protected void Remove(Visual3D visual3d)
        {
            if (Thread.CurrentThread != visual.Dispatcher.Thread)
            {
                visual.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate { Remove(visual3d); });
            }
            else
            {
                visual.Children.Remove(visual3d);

                //MaintainPositions();
                SelectedIndex = List.Count - 1;
            }
        }

        #region Private helper functions

        private Visual3D GetVisual3DForControl(UserControl control)
        {
            lock (controlDictionary)
            {
                if (controlDictionary.ContainsKey(control))
                {
                    return controlDictionary[control];
                }
                else
                {
                    Visual3D ret = GenerateVisual3DForControl(control);
                    controlDictionary.Add(control, ret);
                    visualDictionary.Add(ret, control);
                    return ret;
                }
            }
        }

        private Visual3D GenerateVisual3DForControl(UserControl control)
        {
            Geometry3D geom = null;
            if (MeshPopulating != null)
            {
                MeshPopulating(this, out geom);
            }
            if (isInteractive)
            {
                InteractiveVisual3D iv3d = new InteractiveVisual3D();
                iv3d.Visual = control;
                iv3d.Geometry = geom;
                iv3d.IsBackVisible = !KeepNormals;
                return iv3d;
            }
            else
            {
                Visual vis = control;
                Brush brush = null;

                TagDictionary td = TagDictionary.GetTag(control);
                if (!td.ContainsKey("AsImageSource"))
                {
                    #region THIS IS DAMN SLOW
                    //brush = new VisualBrush(control);
                    #endregion

                    #region Possible better, but not working
                    RenderTargetBitmap rtb = new RenderTargetBitmap((int)control.Width, (int)control.Height, 96d, 96d, PixelFormats.Pbgra32);
                    Canvas c = new Canvas();
                    (Application.Current.MainWindow.Content as Panel).Children.Add(c);
                    //c.Children.Add(control);
                    System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle();
                    rect.Width = 100;
                    rect.Height = 100;
                    rect.RenderTransform = null;
                    rect.ClipToBounds = false;
                    rect.Fill = new SolidColorBrush(Colors.Red);
                    c.Children.Add(rect);
                    c.HorizontalAlignment = HorizontalAlignment.Left;
                    c.VerticalAlignment = VerticalAlignment.Top;
                    c.Margin = new Thickness(0.0f);
                    c.ClipToBounds = true;
                    vis = c;
                    rtb.Render(vis);
                    brush = new ImageBrush(rtb);
                    #endregion
                }
                else
                {
                    brush = new ImageBrush(td.Get<string, BitmapSource>("AsImageSource"));
                }

                Material material = new DiffuseMaterial(brush);
                ModelVisual3D mv3d = new ModelVisual3D();
                mv3d.Content = new GeometryModel3D(geom, material);
                return mv3d;
            }
        }

        private void MaintainPositions()
        {
            lock (controlDictionary)
            {
                int count = visual.Children.Count;
                int i = 0;
                double angle = 0.0f;
                if (count != 0)
                {
                    angle = 360.0f / count;
                }
                foreach (Visual3D v3d in visual.Children)//(KeyValuePair<UserControl, Visual3D> kvp in controlDictionary)
                {
                    Transform3DGroup tranGroup = v3d.Transform as Transform3DGroup;
                    TranslateTransform3D transTrans;
                    RotateTransform3D transRot;
                    ScaleTransform3D transScale;
                    if (tranGroup == null)
                    {
                        v3d.Transform = tranGroup = new Transform3DGroup();
                        tranGroup.Children.Add(transTrans = new TranslateTransform3D());
                        tranGroup.Children.Add(transRot = new RotateTransform3D());
                        tranGroup.Children.Add(transScale = new ScaleTransform3D());
                    }
                    else
                    {
                        transTrans = tranGroup.Children[0] as TranslateTransform3D;
                        transRot = tranGroup.Children[1] as RotateTransform3D;
                        transScale = tranGroup.Children[2] as ScaleTransform3D;
                    }

                    double scXNew = 1.0f;
                    double scYNew = 1.0f;
                    double scZNew = 1.0f;
                    double offX3dNew = 0.0f;
                    double offY3dNew = 0.0f;
                    double offZ3dNew = 0.0f;
                    
                    Duration duration = new Duration(animationSpeed);

                    TagDictionary tag;
                    lock (controlDictionary)
                    {
                        if (controlDictionary.ContainsValue(v3d))
                        {
                            UserControl ctrl = visualDictionary[v3d];
                            tag = TagDictionary.GetTag(ctrl);
                        }
                        else
                        {
                            tag = TagDictionary.GetTag(v3d);
                        }
                    }

                    if (tag != null)
                    {
                        Vector3D scale = tag.Get<string, Vector3D>("Scale", new Vector3D(1.0f, 1.0f, 1.0f));
                        Vector3D offset = tag.Get<string, Vector3D>("Offset");
                        Rotation3D rotation = tag.Get<string, Rotation3D>("Rotation");
                        scXNew = scale.X;
                        scYNew = scale.Y;
                        scZNew = scale.Z;
                        offX3dNew = offset.X * 1.0 / scXNew;
                        offY3dNew = offset.Y * 1.0 / scYNew;
                        if (rotation != null)
                        {
                            Rotation3DAnimation rotAnim = new Rotation3DAnimation(rotation, duration);
                            transRot.BeginAnimation(RotateTransform3D.RotationProperty, rotAnim);
                        }
                    }
                    
                    if (i == selectedIndex)
                    {
                        scXNew *= 2.5f;
                        scYNew *= 2.5f;
                    }

                    if (KeepNormals)
                    {
                        Point p = Math2DHelper.GetPointOfCircle(new Point(axis.X, axis.Z), radius * 1.0 / scZNew, angle * (i - selectedIndex));
                        offX3dNew = p.X;
                        offY3dNew = 0.0;
                        offZ3dNew = p.Y;
                    }
                    else
                    {
                        Rotation3D rot3dNew = new AxisAngleRotation3D(axis, angle * (i - selectedIndex));
                        Rotation3DAnimation rotAnim = new Rotation3DAnimation(rot3dNew, duration);
                        transRot.BeginAnimation(RotateTransform3D.RotationProperty, rotAnim);
                        offZ3dNew = radius * 1.0 / scZNew;
                    }

                    DoubleAnimation transXAnim = new DoubleAnimation(offX3dNew, duration);
                    DoubleAnimation transYAnim = new DoubleAnimation(offY3dNew, duration);
                    DoubleAnimation transZAnim = new DoubleAnimation(offZ3dNew, duration);
                    transTrans.BeginAnimation(TranslateTransform3D.OffsetXProperty, transXAnim);
                    transTrans.BeginAnimation(TranslateTransform3D.OffsetYProperty, transYAnim);
                    transTrans.BeginAnimation(TranslateTransform3D.OffsetZProperty, transZAnim);

                    DoubleAnimation scaleX = new DoubleAnimation(scXNew, duration);
                    DoubleAnimation scaleY = new DoubleAnimation(scYNew, duration);
                    DoubleAnimation scaleZ = new DoubleAnimation(scZNew, duration);

                    transScale.BeginAnimation(ScaleTransform3D.ScaleXProperty, scaleX);
                    transScale.BeginAnimation(ScaleTransform3D.ScaleYProperty, scaleY);
                    transScale.BeginAnimation(ScaleTransform3D.ScaleZProperty, scaleZ);
                    
                    i++;
                }
            }
        }

        #endregion

        #endregion

    }
    */
}